package com.briup.day27.collections;

import java.util.Arrays;

public class MyArrayList<T> implements MyList<T> {

	// 真正用于保存元素的数组，在构造函数中对其进行初始化
	private Object[] data;

	// 表示默认初始值的常量
	public final static int DEFAULT_CAPACITY = 10;

	// 空数组常量
	private final static Object[] EMPTY_DATA = new Object[] {};

	// 当前已经插入元素的数量，也代表新元素应该保存的索引位置
	private int size = 0;

	/*
	 * 给用户提供了两种初始化逻辑： 1、如果用户没有传参，我们给出默认的数组长度 2、如果用户传了参数，就使用该参数作为长度初始化数组
	 */
	public MyArrayList() {
		this(0);
	}

	public MyArrayList(int capacity) {
		if (capacity > 0)
			data = new Object[capacity];
		else if (capacity == 0)
			data = EMPTY_DATA;
		else
			throw new IllegalArgumentException("Illegal Capacity: " + capacity);
	}

	@Override
	public boolean add(T o) {
		// 先调用方法确保容量够用
		ensureCapacity();
		// 把o插入到data数组中
		// data[size++] = o;
		
		data[size] = o;
		size++;
		
		return true;
	}

	// 用来确保数组容量够用
	private void ensureCapacity() { // 扩容数组
		if (size == data.length) {
			// 计算要给数组重新赋值的长度是多少
			// 如果是初始化扩容，用默认的初始长度
			// 否则，长度扩充一倍
			int newCapacity = data.length == 0 ? DEFAULT_CAPACITY 
					: data.length + (data.length >> 1);

			// 先创建一个比之前的数组更大的新数组
			Object[] newData = new Object[newCapacity];

			// 把原数组中的所有元素复制到新数组的对应位置
			System.arraycopy(data, 0, newData, 0, data.length);

			// 让新数组充当data
			data = newData;
		}
	}

	@Override
	public void clear() {
		for (int i = 0; i < size; i++) {
			data[i] = null;
		}
		size = 0;
	}

	@Override
	public boolean contains(Object o) {
		if (o == null) {
			for (int i = 0; i < size; i++) {
				if (data[i] == null)
					return true;
			}
		} else {
			for (int i = 0; i < size; i++) {
				if (o.equals(data[i])) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean isEmpty() {
		return size == 0;
	}

	@Override
	public boolean remove(Object o) {
		if (o == null) {
			for (int i = 0; i < size; i++) {
				if (data[i] == null) {
					fastRemove(i);
					return true;
				}
			}
		} else {
			for (int i = 0; i < size; i++) {
				if (o.equals(data[i])) {
					fastRemove(i);
					return true;
				}
			}
		}
		return false;
	}

	private void fastRemove(int index) {
		int numMoved = size - index - 1;
		System.arraycopy(data, index + 1, data, index, numMoved);
		data[--size] = null;
	}

	@Override
	public int size() {
		return size;
	}

	@Override
	public Object[] toArray() {
		return Arrays.copyOf(data, size);
	}

	@Override
	public MyItr iterator() {
		return new MyItr();
	}

	// 使用内部类实现集合的遍历
	private class MyItr implements MyIterator<T> { // 遍历器
		// position记录当前遍历器对象进行到的位置
		private int position;

		// 判断是否存在下一个元素
		@Override
		public boolean hasNext() {
			return position < size;
		}

		// 给用户提供一个方法用于访问集合中的元素
		@Override
		@SuppressWarnings("unchecked")
		public T next() {
			// 每次返回数组中的一个元素
			return (T) data[position++];
		}
	}

	// 把o插入到集合中index位置
	@Override
	public void add(int index, T o) {
		if (index < 0 || index > size) {
			System.out.println("传入的索引值不合法，插入失败！");
			return;
		}
		ensureCapacity();
		// 把从[index]~[size-1]位置上的所有元素右移一位
		System.arraycopy(data, index, data, index + 1, size - index);
		// 把o插入到index位置
		data[index] = o;
		size++;
	}

	// 获取集合中index位置上的元素
	@Override
	@SuppressWarnings("unchecked")
	public T get(int index) {
		if (index < 0 || index >= size) {
			System.out.println("传入的索引值不合法，获取失败！");
			return null;
		}
		return (T) data[index];
	}

	// 删除集合中index位置上的元素，返回值是被删掉的元素
	@Override
	@SuppressWarnings("unchecked")
	public T remove(int index) {
		if (index < 0 || index >= size) {
			System.out.println("传入的索引值不合法，删除失败！");
			return null;
		}
		
		// 在位移元素之前，把即将要被删除的元素引用暂时保存下来，用于方法的返回
		Object obj = data[index];
		
		// 把从index+1开始到size-1位置的所有元素全部左移一位
		System.arraycopy(data, index + 1, data, index, size - index - 1);
		
		data[--size] = null;
		
		return (T) obj;
	}

	// 将集合中index位置上的元素设为o，返回值为被替换掉的旧元素
	@Override
	@SuppressWarnings("unchecked")
	public T set(int index, T o) {
		if (index < 0 || index >= size) {
			System.out.println("传入的索引值不合法，修改失败！");
			return null;
		}
		// 先用一个临时变量保存即将被替换的元素，用于方法的返回
		Object obj = data[index];
		// 替换元素
		data[index] = o;
		return (T) obj;
	}

}
